package de.unibi.comet.util;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.Stack;
import java.util.EmptyStackException;

public final class Log {
	public enum Level { QUIET, NORMAL, VERBOSE, EVERYTHING, INSANE };
	
	private static Log instance = null;
	private Stack<Long> times;
	private Stack<Long> cpuTimes;
	private boolean timingActive;
	private Level defaultLevel;
	private Level currentLevel;
	private float lastPeriod;
	private float lastPeriodCpu;

	private Log() {
		defaultLevel=Level.NORMAL;
		currentLevel=Level.QUIET;
		timingActive=false;
		times=null;
		lastPeriod=0.0f;
		lastPeriodCpu=0.0f;
	}

	public static Log getInstance() {
		if (instance == null) {
			instance = new Log();
		}
		return instance;
	}
	
	public void startTimer() {
		if (!timingActive) return;
		times.push(System.currentTimeMillis());
        ThreadMXBean tb = ManagementFactory.getThreadMXBean();
        cpuTimes.push(tb.getCurrentThreadCpuTime());
	}
	
	public void stopTimer(String message) {
		if (!timingActive) return;
		String s="";
		try {
			Long t0 = times.pop();
			long t = System.currentTimeMillis() - t0;
			Long t0cput = cpuTimes.pop();
	        ThreadMXBean tb = ManagementFactory.getThreadMXBean();
			long tCpu = tb.getCurrentThreadCpuTime() - t0cput;
			lastPeriod=t/1000.0f;
			lastPeriodCpu=((float)tCpu)*1e-9f;
			for (int i=0; i<times.size(); i++) {
				s+="   ";
			}
			s+=String.format("timer: \"%s\" = %.3f / %.3f", message, lastPeriod, lastPeriodCpu);
		} catch (EmptyStackException e) {
			s+=String.format("timer: \"%s\" = not stored", message);
		}
		System.out.println(s);
	}

	public void restartTimer(String message) {
		if (!timingActive) return;
		stopTimer(message);
		startTimer();
	}

	public Level getLogLevel() {
		return currentLevel;
	}

	public void setLogLevel(Level currentLevel) {
		this.currentLevel = currentLevel;
	}
	
	public boolean isTimingActive() {
		return timingActive;
	}

	public void setTimingActive(boolean timingActive) {
		this.timingActive = timingActive;
		if (timingActive) {
			times = new Stack<Long>();
			cpuTimes = new Stack<Long>();
		}
	}
	
	public void print(Level level, String s) {
		if (level.compareTo(currentLevel)<=0) {
			System.out.println(s);
		}
	}

	public void printNormal(String s) {
		print(Level.NORMAL,s);
	}

	public void printVerbose(String s) {
		print(Level.VERBOSE,s);
	}
	
	public void printEverything(String s) {
		print(Level.EVERYTHING,s);
	}
	
	public void printInsane(String s) {
		print(Level.INSANE,s);
	}
	
	public void print(String s) {
		print(defaultLevel, s);
	}

	public float getLastPeriod() {
		return lastPeriod;
	}

	public float getLastPeriodCpu() {
		return lastPeriodCpu;
	}
}